using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows.Forms;

namespace FluentBinding.WinForms
{
    /// <typeparam name="E">Type of the root entity</typeparam>
    /// <typeparam name="P">Type of the property in the data-bound control</typeparam>
    public class PropertyBinding<E, P> : IPropertyBinding<E, P>
    {
        #region Fields

        protected readonly BindingProxy<E> _bindingProxy;

        #endregion

        #region Constructors

        public PropertyBinding(BindingProxy<E> bindingProxy)
        {
            _bindingProxy = bindingProxy;
        }

        #endregion

        #region IPropertyBinding<E,P> Members

        public IPropertyBinding<E, P> To(IPropertyConvertor<E, P> convertor)
        {
            _bindingProxy.AssignDataMemberConvertor(
                new PropertyConvertorAdapter<E, P>(convertor));

            return this;
        }

        public IPropertyBinding<E, P> To(Func<E, P> selector)
        {
            return To(new EntityToPropertyConvertor<E, P>(selector));
        }

        public IPropertyBinding<E, P> To(Func<E, P> selector, Action<E, P> initializer)
        {
            return To(new EntityToPropertyConvertor<E, P>(selector, initializer));
        }

        public IPropertyBinding<E, P> To<TAnotherP>(
            IPropertyConvertor<E, TAnotherP> convertor,
            ITypeConvertor<TAnotherP, P> typeConvertor)
        {
            _bindingProxy.AssignDataMemberConvertor(
                new PropertyAndTypeConvertorAdapter<E, TAnotherP, P>(convertor, typeConvertor));

            return this;
        }

        public IPropertyBinding<E, P> To<TAnotherP>(
            Func<E, TAnotherP> selector,
            ITypeConvertor<TAnotherP, P> typeConvertor)
        {
            return To(new EntityToPropertyConvertor<E, TAnotherP>(selector), typeConvertor);
        }

        public IPropertyBinding<E, P> To<TAnotherP>(
            Func<E, TAnotherP> selector, Action<E, TAnotherP> initializer,
            ITypeConvertor<TAnotherP, P> typeConvertor)
        {
            return To(new EntityToPropertyConvertor<E, TAnotherP>(selector, initializer), typeConvertor);
        }

        public IPropertyBinding<E, P> WithNotifier<N>(Func<E, N> notifier,
                                                      params Expression<Func<N, object>>[] notifierPropertyExpressions)
            where N : INotifyPropertyChanged
        {
            if (notifierPropertyExpressions.Length == 0)
                _bindingProxy.AddNotifier(notifier(_bindingProxy.Entity));
            else
                notifierPropertyExpressions.ForEach(
                    x => _bindingProxy.AddNotifier(notifier(_bindingProxy.Entity), x.ConvertToMembersPath()));

            return this;
        }

        public IPropertyBinding<E, P> WithEntityPath<N>(Expression<Func<E, N>> entityPathExpression)
            where N : class
        {
            _bindingProxy.EntityPath = entityPathExpression.ConvertToMembersPath();

            return this;
        }

        public IPropertyBinding<E, P> WithAffectedBinding<N>(Expression<Func<E, N>> entityPathExpression)
            where N : class
        {
            _bindingProxy.AddAffectedEntity(entityPathExpression.ConvertToMembersPath());

            return this;
        }

        public IPropertyBinding<E, P> WithDataSourceUpdateMode(DataSourceUpdateMode updateMode)
        {
            _bindingProxy.DataSourceUpdateMode = updateMode;

            return this;
        }

        #endregion
    }
}